interface MoniterCB {
  cb: Function
  once: boolean
}

export interface MoniterMap {
  [k: string]: (...args: any[]) => void
}

export class Moniter<T extends MoniterMap> {
  #pool: Map<keyof T, MoniterCB[]> = new Map()

  /**
   * 事件绑定
   * @param prop 事件名
   * @param cb 回调函数
   * @param once 回调函数只执行一次
   * @returns
   */
  public $on<K extends keyof T>(prop: K, cb: T[K], once: boolean = false) {
    let row = this.#pool.get(prop)
    if (!row) {
      this.#pool.set(prop, (row = []))
    }
    row.push({ cb, once })
    return this
  }

  /**
   * 事件绑定（once 模式）
   * @param prop 事件名
   * @param cb 回调函数
   * @param once 回调函数只执行一次
   * @returns
   */
  public $once<K extends keyof T>(prop: K, cb: T[K]) {
    return this.$on(prop, cb, true)
  }

  /**
   * 事件解绑
   * @param prop 事件名
   * @param cb 回调函数
   * @returns
   */
  public $off<K extends keyof T>(prop: K, cb: T[K]) {
    const row = this.#pool.get(prop)
    if (row) {
      for (let index = 0; index < row.length; index++) {
        if (row[index].cb === cb) {
          row.splice(index--, 1)
        }
      }
    }
    return this
  }

  /**
   * 触发 value 变化回调
   * @param prop 键名
   * @param nVal 新值
   * @param oVal 旧值
   * @returns
   */
  public $emit<K extends keyof T>(prop: K, ...args: Parameters<T[K]>) {
    const row = this.#pool.get(prop)
    if (row) {
      for (let index = 0; index < row.length; index++) {
        row[index].cb(...args)
        if (row[index].once) {
          row.splice(index--, 1)
        }
      }
    }
    return this
  }

  /**
   * 清除 指定/所有 事件回调
   * @param prop 事件名
   * @returns
   */
  public $clear<K extends keyof T>(prop?: K) {
    if (prop) {
      this.#pool.delete(prop)
    } else {
      this.#pool.clear()
    }
    return this
  }
}
